package com.beiding.jsonanalyzer;

import com.beiding.jsonanalyzer.analyze.Segment;
import com.beiding.jsonanalyzer.analyze.SegmentType;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * 分析键
 * 支持
 * a.name
 * a['name']
 * a[1]
 */
public class Key {

    /**
     * 所有的分段
     */
    private final List<Segment> segments;


    private String cacheKeyToText = null;

    private Integer cacheHash;

    public List<Segment> getSegments() {
        return Collections.unmodifiableList(segments);
    }

    /**
     * 创建一个key
     *
     * @param segments
     */
    public Key(List<Segment> segments) {
        this.segments = Collections.unmodifiableList(segments);

    }

    public Key() {
        this.segments = Collections.emptyList();
    }

    public Key(Object... chs) {
        List<Segment> segments = new ArrayList<>(chs.length);
        for (Object ch : chs) {
            if (ch instanceof String) {
                if ("*".equals(ch)) {
                    segments.add(Segment.createAny(true));
                } else {
                    segments.add(Segment.createName((String) ch, ((String) ch).contains(".")));
                }
            } else if (ch instanceof Integer) {
                segments.add(Segment.createIndex((Integer) ch));
            } else {
                throw new RuntimeException("不支持的区段");
            }
        }
        this.segments = Collections.unmodifiableList(segments);

    }


    /**
     * 获取从给定的key开始的相对子key
     *
     * @param from
     * @return
     */
    public Key relativeFrom(Key from) {
        if (!this.isSubOf(from)) {
            throw new RuntimeException(this + " 不是 " + from + " 的下级键");
        }

        return subKey(from.segmentLength());
    }

    /**
     * 判断是否是给定key的子key
     * 两个key的长度相等或者当前key的长度比给定的key的长度要长都可比较
     *
     * @param test
     * @return
     */
    public boolean isSubOf(Key test) {
        List<Segment> segments = test.segments;

        if (this.segments.size() < segments.size()) {
            return false;
        }

        for (int i = 0; i < segments.size(); i++) {
            if (!segments.get(i).equals(this.segments.get(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * 解析生成一个key
     *
     * @param key
     * @return
     */
    public static Key parse(String key) {
        return new Key(parseSegment(key));
    }

    public static List<Segment> parseSegment(String key) {
        if (key == null) {
            return Collections.emptyList();
        }

        key = key.trim();
        if ("".equals(key)) {
            return Collections.emptyList();
        }
        SegmentBuilder segmentBuilder = new SegmentBuilder();
        for (char c : key.toCharArray()) {
            segmentBuilder.add(c);
        }
        segmentBuilder.end();
        return segmentBuilder.segments;
    }

    @Override
    public Key clone() {
        return this.subKey(0);
    }


    public Key childKey(String name) {
        List<Segment> segments = new ArrayList<>(this.segments);
        segments.add(Segment.createName(name, name.contains(".")));
        return new Key(segments);
    }

    public Key childKey(Integer index) {
        List<Segment> segments = new ArrayList<>(this.segments);
        segments.add(Segment.createIndex(index));
        return new Key(segments);
    }

    public Key childKey(Object... chs) {
        List<Segment> segments = new ArrayList<>(this.segments);
        for (Object ch : chs) {
            if (ch instanceof String) {
                segments.add(Segment.createName((String) ch, ((String) ch).contains(".")));
            } else if (ch instanceof Integer) {
                segments.add(Segment.createIndex((Integer) ch));
            } else {
                throw new RuntimeException("不支持的区段");
            }
        }
        return new Key(segments);
    }

    public int segmentLength() {
        return segments.size();
    }

    public int indexOf(String name, int from) {
        for (int i = from; i < segments.size(); i++) {
            Segment segment = segments.get(i);
            if (segment.isName()) {
                if (segment.name.equals(name)) {
                    return i;
                }
            }

        }
        return -1;
    }

    public int indexOf(int index, int from) {
        for (int i = from; i < segments.size(); i++) {
            Segment segment = segments.get(i);
            if (segment.isIndex()) {
                if (index == segment.index) {
                    return i;
                }
            }

        }
        return -1;
    }


    public int lastIndexOf(int index, int from) {
        if (from >= segments.size()) {
            from = segments.size() - 1;
        }

        for (int i = from; i > 0; i--) {
            Segment segment = segments.get(i);
            if (segment.isIndex()) {
                if (index == segment.index) {
                    return i;
                }
            }
        }
        return -1;
    }

    public int lastIndexOf(String name, int from) {
        if (from >= segments.size()) {
            from = segments.size() - 1;
        }

        for (int i = from; i > 0; i--) {
            Segment segment = segments.get(i);
            if (segment.isName()) {
                if (segment.name.equals(name)) {
                    return i;
                }
            }
        }
        return -1;
    }


    public Key concat(Key key) {
        List<Segment> segments = new ArrayList<>(this.segments);
        segments.addAll(key.segments);
        return new Key(segments);
    }

    public Key subKey(int from, int to) {
        if (to < from) {
            throw new RuntimeException("无效区段: " + from + " " + to);
        }

        if (from < 0) {
            from = 0;
        }

        if (to > segments.size()) {
            to = segments.size();
        }

        List<Segment> sg = new ArrayList<>();

        for (int i = from; i < to; i++) {
            sg.add(this.segments.get(i));
        }
        return new Key(sg);
    }

    public Segment segmentAt(int index) {
        if (index >= segments.size()) {
            return null;
        }
        return segments.get(index);
    }

    public Key subKey(int from) {
        return subKey(from, segmentLength());
    }

    private static class SegmentBuilder {

        private List<Segment> segments = new ArrayList<>();

        StringBuilder builder = new StringBuilder();

        int state = -1;

        void add(char c) {

            //不做处理
            if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
                return;
            }

            //state 1 表示在中括号中,-1 初始状态,2 点后,0 正常追加字符
            if (c == '[') {
                if (state == 1) {
                    throw new KeyParseException("不可配对符号:[");
                }
                if (state == 2) {
                    throw new KeyParseException("错误的符号:.");
                }
                if (state == 0) {
                    String trim = builder.toString().trim();
                    if ("*".equals(trim)) {
                        segments.add(Segment.createAny(false));
                    } else {
                        segments.add(Segment.createName(trim, false));
                    }
                    builder.delete(0, builder.length());
                }
                state = 1;
            } else if (c == ']') {
                if (state != 1) {
                    throw new KeyParseException("不可配对符号:]");
                }
                String s = builder.toString();
                builder.delete(0, builder.length());

                s = s.trim();

                handleIn(s);

                state = -1;
            } else if (c == '.') {
                if (state == 0) {

                    //表示正在正常的追加文本
                    String trim = builder.toString().trim();
                    if ("*".equals(trim)) {
                        segments.add(Segment.createAny(false));
                    } else {
                        segments.add(Segment.createName(trim, false));
                    }
                    builder.delete(0, builder.length());
                    state = 2;
                } else if (state == 1) {

                    //表示在前括号内部
                    builder.append(c);

                } else if (state != -1) {
                    throw new KeyParseException("错误的符号:.");
                }
            } else if (c == '\'') {
                if (state != 1) {
                    throw new KeyParseException("错误的符号:'");
                }
                builder.append(c);
            } else {

                //在中括号中不检查,任意添加. 中括号会最终解析时检查
            /*    if (state != 1) {
                    if (!allowChars.contains(c)) {
                        throw new KeyParseException("错误的符号:" + c);
                    }
                }*/

                builder.append(c);
                if (state != 1) {
                    state = 0;
                }
            }
        }

        void end() {
            if (builder.length() > 0) {
                if (state == 1) {

                    String s = builder.toString();
                    s = s.trim();

                    //['name'] 类型的
                    handleIn(s);
                } else if (state == 0) {
                    String trim = builder.toString().trim();
                    if ("*".equals(trim)) {
                        segments.add(Segment.createAny(false));
                    } else {
                        segments.add(Segment.createName(trim, false));
                    }
                } else {
                    throw new KeyParseException("错误的key表达式: " + builder.toString());
                }
            }
        }


        private void handleIn(String s) {
            //['name'] 类型的
            if (s.startsWith("'") && s.endsWith("'")) {
                s = s.substring(1, s.length() - 1);
                segments.add(Segment.createName(s, true));
            } else
                //["name"] 类型的
                if (s.startsWith("\"") && s.endsWith("\"")) {
                    s = s.substring(1, s.length() - 1);
                    segments.add(Segment.createName(s, true));
                } else {

                    //尝试解析

                    if ("*".equals(s)) {
                        segments.add(Segment.createAny(true));
                    } else {
                        try {
                            // [1] 类型的
                            int n = Integer.parseInt(s);
                            segments.add(Segment.createIndex(n));
                        } catch (NumberFormatException e) {
                            throw new KeyParseException("不支持的表达式: " + s);
                        }
                    }

                }
        }

//
//        private void buildSeg3() {
//
//        }
//
//
//        interface CharHandle {
//            void handle(SegmentBuilder segmentBuilder, char c, int pos);
//        }
//
//        /**
//         * 状态模式
//         */
//        static abstract class SegState {
//
//            private Map<Character, CharHandle> charHandleMap;
//
//            private CharHandle defaultCharHandle;
//
//            SegState() {
//                charHandleMap = new HashMap<>();
//                buildCharHandleMap(charHandleMap);
//                defaultCharHandle = defaultCharHandle();
//            }
//
//            abstract void buildCharHandleMap(Map<Character, CharHandle> charHandleMap);
//
//            abstract CharHandle defaultCharHandle();
//
//            void handle(SegmentBuilder segmentBuilder, char c, int pos) {
//                CharHandle charHandle = charHandleMap.get(c);
//                if (charHandle != null) {
//                    charHandle.handle(segmentBuilder, c, pos);
//                } else {
//                    defaultCharHandle.handle(segmentBuilder, c, pos);
//                }
//            }
//
//        }
//
//        /**
//         * 一般key
//         * 该状态下可构建
//         * user.name
//         * 形式的段
//         */
//        static SegState STATE_3 = new SegState() {
//            @Override
//            void buildCharHandleMap(Map<Character, CharHandle> charHandleMap) {
//
//                charHandleMap.put(
//                        '[',
//                        (segmentBuilder, c, pos) -> {
//                            segmentBuilder.buildSeg3();
//                            segmentBuilder.state = segmentBuilder.STATE_1;
//                        });
//                charHandleMap.put(
//                        '.',
//                        (segmentBuilder, c, pos) -> {
//                            segmentBuilder.buildSeg3();
//                            segmentBuilder.state = segmentBuilder.STATE_2;
//                        });
//
//                charHandleMap.put(
//                        '\'',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//
//                charHandleMap.put(
//                        ']',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//
//            }
//
//            @Override
//            CharHandle defaultCharHandle() {
//                return (segmentBuilder, c, pos) -> {
//                    segmentBuilder.builder.append(c);
//                };
//            }
//        };


//        /**
//         * 在中括号中
//         */
//        static SegState STATE_1 = new SegState() {
//            @Override
//            void buildCharHandleMap(Map<Character, CharHandle> charHandleMap) {
//
//                charHandleMap.put(
//                        '[',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//                charHandleMap.put(
//                        '.',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//
//                charHandleMap.put(
//                        '\'',
//                        (segmentBuilder, c, pos) -> {
//
//                            //当前字符丢弃
//
//                            //进入字符串构建
//                            segmentBuilder.state = SegmentBuilder.STATE_0;
//                        });
//
//                charHandleMap.put(
//                        ']',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//
//            }
//
//            @Override
//            CharHandle defaultCharHandle() {
//                return (segmentBuilder, c, pos) -> {
//                    //数字构建
//                    if (c >= '0' && c <= '9') {
//                        segmentBuilder.builder.append(c);
//                    } else {
//                        throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                    }
//                };
//            }
//
//        };
//        static SegState STATE_4 = new SegState() {
//            @Override
//            void buildCharHandleMap(Map<Character, CharHandle> charHandleMap) {
//
//                charHandleMap.put(
//                        '[',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//                charHandleMap.put(
//                        '.',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//
//                charHandleMap.put(
//                        '\'',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//
//                charHandleMap.put(
//                        ']',
//                        (segmentBuilder, c, pos) -> {
//                            throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                        });
//
//            }
//
//            @Override
//            CharHandle defaultCharHandle() {
//                return (segmentBuilder, c, pos) -> {
//                    //数字构建
//                    if (c >= '0' && c <= '9') {
//                        segmentBuilder.builder.append(c);
//                    } else {
//                        throw new KeyParseException("错误符号:" + c + " 位置:" + pos);
//                    }
//                };
//            }
//
//        };
//
//        /**
//         * 在点后
//         */
//        static SegState STATE_2 = (segmentBuilder, c, pos) -> {
//
//        };
//
//
//        /**
//         * 字符串构建中
//         */
//        static SegState STATE_0 = (segmentBuilder, c, pos) -> {
//
//        };

    }

    @Override
    public String toString() {
        if (cacheKeyToText == null) {
            if (segments.size() == 0) {
                cacheKeyToText = "$self";
                return cacheKeyToText;
            }


            StringBuilder builder = new StringBuilder();
            Iterator<Segment> iterator = segments.iterator();
            if (iterator.hasNext()) {
                Segment next = iterator.next();
                if (next.type == SegmentType.NAME) {

                    if (next.integral) {
                        builder.append("['").append(next.name).append("']");
                    } else {
                        builder.append(next.name);
                    }
                } else if (next.type == SegmentType.INDEX) {
                    builder.append("[").append(next.index).append("]");
                } else if (next.type == SegmentType.ANY) {
                    builder.append("*");
                }
                while (iterator.hasNext()) {
                    next = iterator.next();
                    if (next.type == SegmentType.NAME) {

                        if (next.integral) {
                            builder.append("['").append(next.name).append("']");
                        } else {
                            builder.append(".").append(next.name);
                        }
                    } else if (next.type == SegmentType.INDEX) {
                        builder.append("[").append(next.index).append("]");
                    } else if (next.type == SegmentType.ANY) {
                        builder.append("*");
                    }
                }
            }
            cacheKeyToText = builder.toString();
        }
        return cacheKeyToText;
    }

    public Key parentKey() {
        return subKey(0, segmentLength() - 1);
    }


    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Key)) {
            return false;
        }

        Key ko = (Key) obj;

        //不相等直接返回
        if (ko.hashCode() != hashCode()) {
            return false;
        }

        int a = ko.segmentLength();
        int b = segmentLength();

        if (a != b) {
            return false;
        }
        for (int i = 0; i < a; i++) {
            if (!segmentAt(i).equals(ko.segmentAt(i))) {
                return false;
            }
        }
        return true;

    }

    @Override
    public int hashCode() {

        if (cacheHash == null) {
            int ch = 0;
            for (Segment segment : segments) {
                ch ^= segment.hashCode();
            }
            cacheHash = ch;
        }

        return cacheHash;
    }

    public boolean contains(Key s) {
        return indexOf(s) > -1;
    }

    public boolean endsWith(Key suffix) {
        return startsWith(suffix, segmentLength() - suffix.segmentLength());
    }

    public boolean startsWith(Key prefix) {
        return startsWith(prefix, 0);
    }

    public boolean startsWith(Key prefix, int toffset) {
        Segment ta[] = toSegArray();
        int to = toffset;
        Segment pa[] = prefix.toSegArray();
        int po = 0;
        int pc = prefix.segmentLength();
        // Note: toffset might be near -1>>>1.
        if ((toffset < 0) || (toffset > segmentLength() - pc)) {
            return false;
        }
        while (--pc >= 0) {
            if (!ta[to++].equals(pa[po++])) {
                return false;
            }
        }
        return true;
    }

    public int indexOf(Key str) {
        return indexOf(str, 0);
    }

    public int indexOf(Key str, int fromIndex) {
        return indexOf(this.toSegArray(), 0, this.segmentLength(),
                str.toSegArray(), 0, str.segmentLength(), fromIndex);
    }

    static int indexOf(Segment[] source, int sourceOffset, int sourceCount,
                       Segment[] target, int targetOffset, int targetCount,
                       int fromIndex) {
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (targetCount == 0) {
            return fromIndex;
        }

        Segment first = target[targetOffset];
        int max = sourceOffset + (sourceCount - targetCount);

        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            /* Look for first character. */
            if (!source[i].equals(first)) {
                while (++i <= max && !source[i].equals(first)) {
                }
                ;
            }

            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                int j = i + 1;
                int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j].equals(target[k]); j++, k++) {
                }
                ;

                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    }


    public int lastIndexOf(Key str) {
        return lastIndexOf(str, this.segmentLength());
    }


    public int lastIndexOf(Key str, int fromIndex) {
        return lastIndexOf(this.toSegArray(), 0, this.segmentLength(),
                str.toSegArray(), 0, str.segmentLength(), fromIndex);
    }

    static int lastIndexOf(Segment[] source, int sourceOffset, int sourceCount,
                           Segment[] target, int targetOffset, int targetCount,
                           int fromIndex) {
        /*
         * Check arguments; return immediately query possible. For
         * consistency, don't check for null str.
         */
        int rightIndex = sourceCount - targetCount;
        if (fromIndex < 0) {
            return -1;
        }
        if (fromIndex > rightIndex) {
            fromIndex = rightIndex;
        }
        /* Empty string always matches. */
        if (targetCount == 0) {
            return fromIndex;
        }

        int strLastIndex = targetOffset + targetCount - 1;
        Segment strLastChar = target[strLastIndex];
        int min = sourceOffset + targetCount - 1;
        int i = min + fromIndex;

        startSearchForLastChar:
        while (true) {
            while (i >= min && !source[i].equals(strLastChar)) {
                i--;
            }
            if (i < min) {
                return -1;
            }
            int j = i - 1;
            int start = j - (targetCount - 1);
            int k = strLastIndex - 1;

            while (j > start) {
                if (!source[j--].equals(target[k--])) {
                    i--;
                    continue startSearchForLastChar;
                }
            }
            return start - sourceOffset + 1;
        }
    }

    private Segment[] toSegArray() {
        Segment[] arr = new Segment[this.segmentLength()];
        this.segments.toArray(arr);
        return arr;
    }

    public static class KeyParseException extends RuntimeException {
        public KeyParseException(String message) {
            super(message);
        }
    }

}
